package com.mrd.serverconnect;

/**
 * Created by cww on 2019/10/8.
 */

import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;

import com.google.gson.Gson;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.Enumeration;

/**
 * 设备等待搜索类
 * Created by zjun on 2016/9/4.
 */
public abstract class DeviceWaitingSearch extends Thread {
    private final String TAG = "DeviceWaitingSearch";

    private static final int DEVICE_FIND_PORT = 8824;
    private static final int DEVICE_TCP_PORT = 6090;
    private static final int RECEIVE_TIME_OUT = 1500; // 接收超时时间，应小于等于主机的超时时间1500
    private static final int RESPONSE_DEVICE_MAX = 200; // 响应设备的最大个数，防止UDP广播攻击

    private static final byte PACKET_TYPE_FIND_DEVICE_REQ_10 = 0x10; // 搜索请求
    private static final byte PACKET_TYPE_FIND_DEVICE_RSP_11 = 0x11; // 搜索响应
    private static final byte PACKET_TYPE_FIND_DEVICE_CHK_12 = 0x12; // 搜索确认

    private static final byte PACKET_DATA_TYPE_DEVICE_NAME_20 = 0x20;
    private static final byte PACKET_DATA_TYPE_DEVICE_ROOM_21 = 0x21;

    private Context mContext;
    private String deviceName, deviceRoom;

    private final byte head[] = {(byte) 0xF7, (byte) 0xFB, (byte) 0xF9, (byte) 0xA2};
    private final byte action = 0x31;
    private final byte verifyAction = 0x32;

    public DeviceWaitingSearch(Context context, String name, String room) {
        mContext = context;
        deviceName = name;
        deviceRoom = room;
    }

    class ClientPKG {
        byte head[] = new byte[4];
        byte action;
        short len;
        short port;
        byte[] hubSn = new byte[13];
    }


    @Override
    public void run() {
        DatagramSocket socket = null;
        try {
            socket = new DatagramSocket(DEVICE_FIND_PORT);

            while (true) {
                // 等待主机的搜索
                byte[] data = new byte[128];
                DatagramPacket pack = new DatagramPacket(data, data.length);
                socket.receive(pack);
                byte[] receiveData = pack.getData();

          /*      Log.e(TAG, "run: " + len);

                String command = new String(data, 0, len, Charset.defaultCharset());
                Log.e(TAG, "command: " + command);
*/
                String ip = pack.getAddress().getHostAddress();

                ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(data);
                DataInputStream dataInputStream = new DataInputStream(byteArrayInputStream);
                ClientPKG clientPKG = new ClientPKG();
                dataInputStream.read(clientPKG.head);
                if (!(head[0] == clientPKG.head[0]
                        && head[1] == clientPKG.head[1]
                        && head[2] == clientPKG.head[2]
                        && head[3] == clientPKG.head[3])) {
                    Log.e(TAG, ip + " <---is not our device");
                    continue;
                }


                //读取接收数据
                clientPKG.action = dataInputStream.readByte();
                clientPKG.len = dataInputStream.readShort();
                clientPKG.port = dataInputStream.readShort();
                dataInputStream.readByte();
                dataInputStream.read(clientPKG.hubSn);
                String hubSn = new String(clientPKG.hubSn).trim();
                Log.e(TAG, " <---收到广播M6 ip:" + ip + "  sn: " + hubSn);

                //if (verifySearchData(pack)) {
                // 返回当前设备的信息给客流设备 让客流来连接中控
                ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
                DataOutputStream dataOutputStream = new DataOutputStream(byteOutput);
                dataOutputStream.write(head);
                dataOutputStream.write(verifyAction);
                dataOutputStream.writeShort(29);
                dataOutputStream.write("0000000000000".getBytes());
                dataOutputStream.write(clientPKG.hubSn);
                dataOutputStream.write(0x01);
                dataOutputStream.writeShort(6090);
                dataOutputStream.flush();
                byte[] bytes = byteOutput.toByteArray();
                sendData(ip, clientPKG.port, bytes);
               /* byte[] sendData = packData();
                String sendCommand = new String(sendData, 0, sendData.length, Charset.defaultCharset());
                Log.e(TAG, "command: " + sendCommand);

                DatagramPacket sendPack = new DatagramPacket(sendData, sendData.length, pack.getAddress(), pack.getPort());
                Log.i(TAG, "@@@cww: 给主机回复信息");
                socket.send(sendPack);
                Log.i(TAG, "@@@cww: 等待主机接收确认");
                socket.setSoTimeout(RECEIVE_TIME_OUT);*/
               /* try {
                    socket.receive(pack);
                    //if (verifyCheckData(pack)) {
                    Log.i(TAG, "@@@zjun: 确认成功");
                    onDeviceSearched((InetSocketAddress) pack.getSocketAddress());
                    break;
                    //}
                } catch (SocketTimeoutException e) {

                }
                socket.setSoTimeout(0); // 连接超时还原成无穷大，阻塞式接收
                //}*/
            }
        } catch (IOException e) {
            Log.e(TAG,"socket error",e);
        } finally {
            if (socket != null) {
                socket.close();
            }
        }
    }

    private void sendData(String host, int port, byte[] data) {
        DatagramSocket clientSocket;
        try {
            Thread.sleep(50);
            InetSocketAddress serverAddress = new InetSocketAddress(host, port);
            DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress);
            clientSocket = new DatagramSocket();
            clientSocket.send(packet);
            clientSocket.close();
            Log.e(TAG, "  --->回复客户端消息端口：" + host + ":" + port +
                    " \n DATA:" + bytesToHexString(data, data.length));
        } catch (InterruptedException e) {
            // e.printStackTrace();
            Log.e(TAG, "  --->回复客户端消息端口：InterruptedException: " + e.getMessage());
        } catch (IOException e) {
            Log.e(TAG, "  --->回复客户端消息端口：IOException: " + e.getMessage());
        }

        DatagramSocket clientSocket2;
        try {
            Thread.sleep(50);
            InetSocketAddress serverAddress = new InetSocketAddress("255.255.255.255", port);
            DatagramPacket packet = new DatagramPacket(data, data.length, serverAddress);
            clientSocket2 = new DatagramSocket();
            clientSocket2.send(packet);
            clientSocket2.close();
            Log.e(TAG, "  --->回复客户端消息端口2：" + "255.255.255.255" + ":" + port +
                    " \n DATA:" + bytesToHexString(data, data.length));
        } catch (InterruptedException e) {
            // e.printStackTrace();
        } catch (IOException e) {
        }
    }

    /**
     * 把字节数组转换成16进制字符串
     *
     * @param bArray
     * @return
     */
    private String bytesToHexString(byte[] bArray, int len) {
        StringBuilder sb = new StringBuilder();
        String sTemp;
        for (int i = 0; i < len; i++) {
            sTemp = Integer.toHexString(0xFF & bArray[i]);
            sb.append(" 0x");
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }

    /**
     * 当设备被发现时执行
     */
    public abstract void onDeviceSearched(InetSocketAddress socketAddr);

    /**
     * 打包响应报文
     * 协议：$ + packType(1) + data(n)
     * data: 由n组数据，每组的组成结构type(1) + length(4) + data(length)
     * type类型中包含name、room类型，但name必须在最前面
     */
    private byte[] packData() {
        byte[] data = new byte[1024];
        int offset = 0;
        /*data[offset++] = '$';
        data[offset++] = PACKET_TYPE_FIND_DEVICE_RSP_11;

        byte[] temp = getBytesFromType(PACKET_DATA_TYPE_DEVICE_NAME_20, deviceName);
        System.arraycopy(temp, 0, data, offset, temp.length);
        offset += temp.length;

        temp = getBytesFromType(PACKET_DATA_TYPE_DEVICE_ROOM_21, deviceRoom);
        System.arraycopy(temp, 0, data, offset, temp.length);
        offset += temp.length;

        byte[] retVal = new byte[offset];
        System.arraycopy(data, 0, retVal, 0, offset);

        return retVal;*/

        ResponseData responseData = new ResponseData();
        try {
            responseData.setServer(getLocalIPAddress());
        } catch (SocketException e) {
            e.printStackTrace();
        }
        responseData.setPort(DEVICE_TCP_PORT);
        responseData.setAction(1);
        responseData.setCmdId(10000);
        String str = new Gson().toJson(responseData);
        byte[] bytes = str.getBytes();
        return bytes;
    }

    private byte[] getBytesFromType(byte type, String val) {
        byte[] retVal = new byte[0];
        if (val != null) {
            byte[] valBytes = val.getBytes(Charset.forName("UTF-8"));
            retVal = new byte[5 + valBytes.length];
            retVal[0] = type;
            retVal[1] = (byte) valBytes.length;
            retVal[2] = (byte) (valBytes.length >> 8);
            retVal[3] = (byte) (valBytes.length >> 16);
            retVal[4] = (byte) (valBytes.length >> 24);
            System.arraycopy(valBytes, 0, retVal, 5, valBytes.length);
        }
        return retVal;
    }

    /**
     * 校验搜索数据
     * 协议：$ + packType(1) + sendSeq(4)
     * packType - 报文类型
     * sendSeq - 发送序列
     */
    private boolean verifySearchData(DatagramPacket pack) {
        if (pack.getLength() != 6) {
            return false;
        }

        byte[] data = pack.getData();
        int offset = pack.getOffset();
        int sendSeq;
        if (data[offset++] != '$' || data[offset++] != PACKET_TYPE_FIND_DEVICE_REQ_10) {
            return false;
        }
        sendSeq = data[offset++] & 0xFF;
        sendSeq |= (data[offset++] << 8);
        sendSeq |= (data[offset++] << 16);
        sendSeq |= (data[offset++] << 24);
        return sendSeq >= 1 && sendSeq <= 3;
    }

    /**
     * 校验确认数据
     * 协议：$ + packType(1) + sendSeq(4) + deviceIP(n<=15)
     * packType - 报文类型
     * sendSeq - 发送序列
     * deviceIP - 设备IP，仅确认时携带
     */
    private boolean verifyCheckData(DatagramPacket pack) {
        if (pack.getLength() < 6) {
            return false;
        }

        byte[] data = pack.getData();
        int offset = pack.getOffset();
        int sendSeq;
        if (data[offset++] != '$' || data[offset++] != PACKET_TYPE_FIND_DEVICE_CHK_12) {
            return false;
        }
        sendSeq = data[offset++] & 0xFF;
        sendSeq |= (data[offset++] << 8);
        sendSeq |= (data[offset++] << 16);
        sendSeq |= (data[offset++] << 24);
        if (sendSeq < 1 || sendSeq > RESPONSE_DEVICE_MAX) {
            return false;
        }

        String ip = new String(data, offset, pack.getLength() - offset, Charset.forName("UTF-8"));
        Log.i(TAG, "@@@zjun: ip from host=" + ip);
        return ip.equals(getOwnWifiIP());
    }


    public static String getLocalIPAddress() throws SocketException {
        try {
            for (Enumeration en = NetworkInterface.getNetworkInterfaces();
                 en.hasMoreElements(); ) {
                NetworkInterface intf = (NetworkInterface) en.nextElement();
                for (Enumeration enumIpAddr = intf.getInetAddresses();
                     enumIpAddr.hasMoreElements(); ) {
                    InetAddress inetAddress = (InetAddress) enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && (inetAddress instanceof Inet4Address)) {
                        return inetAddress.getHostAddress().toString();
                    }
                }
            }
        } catch (SocketException ex) {

        }
        return null;
    }

    /**
     * 获取本机在Wifi中的IP
     */
    private String getOwnWifiIP() {
        WifiManager wm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
        if (!wm.isWifiEnabled()) {
            return "";
        }

        // 需加权限：android.permission.ACCESS_WIFI_STATE
        WifiInfo wifiInfo = wm.getConnectionInfo();
        int ipInt = wifiInfo.getIpAddress();
        String ipAddr = int2Ip(ipInt);
        Log.i(TAG, "@@@zjun: 本机IP=" + ipAddr);
        return int2Ip(ipInt);
    }

    /**
     * 把int表示的ip转换成字符串ip
     */
    private String int2Ip(int i) {
        return String.format("%d.%d.%d.%d", i & 0xFF, (i >> 8) & 0xFF, (i >> 16) & 0xFF, (i >> 24) & 0xFF);
    }

}